package fec
import (
	"github.com/lucas-clemente/quic-go/internal/protocol"
	"github.com/lucas-clemente/quic-go/internal/wire"
	"errors"
	"github.com/lucas-clemente/quic-go/internal/utils"
	"log"
)

type FECContainer interface {
	ShouldBeSent(RedundancyController) bool
	AddPacket([]byte,  *wire.Header)
	PrepareToSend() error
	AddRepairSymbol(*RepairSymbol) error
	HasPacket(protocol.PacketNumber) bool
	GetPacketOffset(protocol.PacketNumber) byte
	CurrentNumberOfPackets() int
	SetRepairSymbols([]*RepairSymbol)
	GetRepairSymbols() []*RepairSymbol
	GetPackets() [][]byte
}

var _ FECContainer = &FECWindow{}

type FECWindow struct {
	WindowSize					 uint8
	RepairSymbols        []*RepairSymbol
	nSentRepairSymbols   uint8
	framesOffset         uint8
	offsetInSymbol       protocol.ByteCount
	packets              utils.PacketsRingBuffer
	packetIndexes        map[protocol.PacketNumber]int
	version 						 protocol.VersionNumber
	currentNumberOfPackets 			int
	currentIndex								protocol.FECPayloadID
	repairSymbolsInterval 			int
	lastSymbolSent						  protocol.FECPayloadID
	TotalNumberOfPackets 				int
	TotalNumberOfRepairSymbols 	int
}

func NewFECWindow(windowSize uint8, repairSymbolsInterval int,version protocol.VersionNumber) *FECWindow {
	return &FECWindow{
		WindowSize: 		windowSize,
		packetIndexes: 	make(map[protocol.PacketNumber]int),
		packets:				utils.NewPacketsRingBuffer(windowSize),
		version: 				version,
		currentIndex:   protocol.FECPayloadID(0),
		repairSymbolsInterval: repairSymbolsInterval,
	}
}

var FECWindowPacketAddedToFullFECWindow = errors.New("FECWindow: A packet has been added to an already full FEC Payload")

func (f *FECWindow) ShouldBeSent(c RedundancyController) bool {
	return /*uint32(f.currentNumberOfPackets) == uint32(c.GetNumberOfDataSymbols()) &&*/ f.currentIndex - f.lastSymbolSent >= protocol.FECPayloadID(f.repairSymbolsInterval)
}


func (f *FECWindow) AddPacket(packet []byte,  hdr *wire.Header) {
	if hdr.GetFECPayloadID() != f.currentIndex + 1 {
		log.Printf("receive %d vs %d", hdr.GetFECPayloadID(), f.currentIndex)
		panic("wrong fec payload id received")
	}
	if _, ok := f.packetIndexes[hdr.PacketNumber] ; ok {
		return
	}
	packetCopy := make([]byte, protocol.MaxReceivePacketSize)[:len(packet)]
	copy(packetCopy, packet)
	f.packetIndexes[hdr.PacketNumber] = f.packets.AddPacket(packetCopy)
	f.currentNumberOfPackets = f.packets.CurrentSize()
	f.currentIndex++
	return
}

// Must be called before sending the group
func (f *FECWindow) PrepareToSend() error {
	f.lastSymbolSent = f.currentIndex
	return nil
}

func (f *FECWindow) AddRepairSymbol(symbol *RepairSymbol) error {
	f.RepairSymbols = append(f.RepairSymbols, symbol)
	return nil
}

func (f *FECWindow) HasPacket(packetNumber protocol.PacketNumber) bool {
	_, ok := f.packetIndexes[packetNumber]
	return ok
}

func (f *FECWindow) GetPacketOffset(packetNumber protocol.PacketNumber) byte {
	return byte(f.packetIndexes[packetNumber])
}

func (f *FECWindow) CurrentNumberOfPackets() int {
	return f.currentNumberOfPackets
}

func (f *FECWindow) SetRepairSymbols(symbols []*RepairSymbol) {
	for _, s := range symbols {
		s.FECPayloadID = f.currentIndex
		s.NumberOfRepairSymbols = uint8(len(symbols))
		s.NumberOfPackets = uint8(f.CurrentNumberOfPackets())
	}
	f.RepairSymbols = symbols
}

func (f *FECWindow) GetRepairSymbols() []*RepairSymbol {
	return f.RepairSymbols
}

func (f *FECWindow) GetPackets() [][]byte {
	return f.packets.GetAll()
}